#include "ComponentStareBehavior.h"
#include "Components/ComponentCreature.h"
#include "Components/ComponentBody.h"
#include "Components/ComponentCreatureSounds.h"
#include "Components/ComponentLocomotion.h"
#include "Components/ComponentCreatureModel.h"

#include "ComponentPathfinding.h"
#include "Subsystems/SubsystemTerrain.h"
#include "Subsystems/SubsystemTimeOfDay.h"
#include "Subsystems/SubsystemBodies.h"

#include "GameRandom.hpp"
#include "Engine/MathUtils.hpp"
#include "Log.h"
#include "Terrain/ScSimplexNoise.h"

#include "GameSingleton.h"
#include "GameRegistry.hpp"
#include "GameThreadPool.hpp"

#include <iostream>

namespace PocketSurvival
{

    // ************** 【状态机】 **************

    ComponentStareBehavior::StateMachine::StateMachine(ComponentStareBehavior *stareBehavior) : m_stareBehavior(stareBehavior), EasyMachine(MachineState::Inactive)
    {
        enter();
    }

    void ComponentStareBehavior::StateMachine::enter()
    {
        switch (m_currentState)
        {
        case MachineState::Inactive:
            m_stareBehavior->m_importanceLevel = 0.0f;
            break;
        case MachineState::Stare:
            m_stareBehavior->m_stareEndTime = m_stareBehavior->subsystemTimeOfDay->getGameTime() + GameSingleton::gameRandom->getFloat(6.0f, 12.0f);
            if (m_stareBehavior->m_target != entt::null)
            {
                Vector3 position;
                m_stareBehavior->componentCreature->componentBody->getPosition(position);

                if (GameSingleton::gameRegistry->valid(m_stareBehavior->m_target))
                {
                    ComponentBody *body = GameSingleton::gameRegistry->get<ComponentBody*>(m_stareBehavior->m_target);

                    Vector3 bodyPosition;
                    body->getPosition(bodyPosition);

                    Vector3 v = Vector3::Normalize(bodyPosition - position);
                    m_stareBehavior->componentPathfinding->setDestination(position + 1.1f * v, GameSingleton::gameRandom->getFloat(0.3f, 0.4f), 1.0f, 0, false, true, false, nullptr);
                    if (GameSingleton::gameRandom->getFloat(0.0f, 1.0f) < 0.5f)
                    {
                        // m_stareBehavior->componentCreature->componentCreatureSounds->playAttackSound
                    }
                }
                else
                {
                    m_stareBehavior->m_target = entt::null;
                }
            }
            break;
        }
    }

    void ComponentStareBehavior::StateMachine::update(float dt)
    {
        switch (m_currentState)
        {
        case MachineState::Inactive:
            MACHINE_DELEGATE()
            {
                if (m_stareBehavior->subsystemTimeOfDay->getGameTime() > m_stareBehavior->m_stareEndTime + 8.0f && GameSingleton::gameRandom->getFloat(0.0f, 1.0f) < 1.0f * dt)
                {
                    m_stareBehavior->m_target = m_stareBehavior->findTarget();
                    if (m_stareBehavior->m_target != entt::null)
                    {
                        if (GameSingleton::gameRegistry->valid(m_stareBehavior->m_target))
                        {
                            ComponentPlayer **playerPtr = GameSingleton::gameRegistry->try_get<ComponentPlayer*>(m_stareBehavior->m_target);
                            float probability = (playerPtr != nullptr) ? 1.0f : 0.25f;
                            if (GameSingleton::gameRandom->getBool(probability))
                            {
                                m_stareBehavior->m_importanceLevel = GameSingleton::gameRandom->getFloat(3.0f, 5.0f);
                            }
                        }
                    }
                }
                if (m_stareBehavior->isActive())
                {
                    changeTo(MachineState::Stare);
                }
            }
            break;
        case MachineState::Stare:
            MACHINE_DELEGATE()
            {
                if (m_stareBehavior->isActive() == false ||
                    m_stareBehavior->m_target == entt::null ||
                    m_stareBehavior->componentPathfinding->isStuck ||
                    m_stareBehavior->subsystemTimeOfDay->getGameTime() > m_stareBehavior->m_stareEndTime)
                {
                    changeTo(MachineState::Inactive);
                    return;
                }
                else if (GameSingleton::gameRandom->getFloat(0.0f, 1.0f) < 1.0f * dt)
                {
                    if (GameSingleton::gameRegistry->valid(m_stareBehavior->m_target))
                    {
                        ComponentBody **bodyPtr = GameSingleton::gameRegistry->try_get<ComponentBody*>(m_stareBehavior->m_target);
                        if (bodyPtr != nullptr)
                        {
                            ComponentCreature *creature = (*bodyPtr)->componentCreature;
                            if (m_stareBehavior->scoreTarget(creature) <= 0.0f)
                            {
                                changeTo(MachineState::Inactive);
                            }
                            else
                            {
                                m_stareBehavior->componentCreature->componentCreatureModel->lookAtOrder = std::make_shared<Vector3>(creature->componentCreatureModel->getEyePosition());
                            }
                        }
                    }
                }
            }
            break;
        }
    }

    void ComponentStareBehavior::StateMachine::leave() {}

    // **************************************



    ComponentStareBehavior::~ComponentStareBehavior()
    {
        delete m_stateMachine;
    }

    float ComponentStareBehavior::getImportanceLevel()
    {
        return m_importanceLevel;
    }

    entt::entity ComponentStareBehavior::findTarget()
    {
        Vector3 position;
        componentCreature->componentBody->getPosition(position);

        std::vector<ComponentBody*> bodyVec;
        subsystemBodies->findBodiesAroundPoint(Vector2(position.posX, position.posZ), m_stareRange, bodyVec);
        entt::entity result = entt::null;
        float num = 0.0f;
        for (ComponentBody *body : bodyVec)
        {
            if (body->componentCreature != nullptr)
            {
                float num2 = scoreTarget(body->componentCreature);
                if (num2 > num)
                {
                    result = body->entity;
                    num = num2;
                }
            }
        }
        return result;
    }

    float ComponentStareBehavior::scoreTarget(ComponentCreature *creature)
    {
        if (creature != componentCreature)
        {
            Vector3 position;
            Vector3 position2;
            componentCreature->componentBody->getPosition(position);
            creature->componentBody->getPosition(position2);

            float num = Vector3::Distance(position, position2);
            float num2 = m_stareRange - num;
            if (GameSingleton::gameRandom->getFloat(0.0f, 1.0f) < 0.66f && componentCreature->componentBody->componentPlayer != nullptr)
            {
                num2 *= 100.0f;
            }
            return num2;
        }
        return 0.0f;
    }


    void ComponentStareBehavior::update(float dt)
    {
        m_stateMachine->update(dt);
    }

    static const std::string componentName = "StareBehavior";
    const std::string &ComponentStareBehavior::getName() const
    {
        return componentName;
    }
    const std::string &ComponentStareBehavior::GetName()
    {
        return componentName;
    }
    void ComponentStareBehavior::load(const nlohmann::json &jsonData)
    {
        m_stareRange = jsonData["StareRange"].get<float>();

        componentCreature = GameSingleton::gameRegistry->get<ComponentCreature *>(entity);
        componentPathfinding = GameSingleton::gameRegistry->get<ComponentPathfinding *>(entity);

        subsystemTimeOfDay = GameSingleton::singletonPtr->getSingleton<SubsystemTimeOfDay>();
        subsystemBodies = GameSingleton::singletonPtr->getSingleton<SubsystemBodies>();

        m_stateMachine = new StateMachine(this);
    }

}
